/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package me.panpf.sketch.sample.widget.fraction;

import cn.bingoogolapple.refreshlayout.BgaStickinessRefreshViewHolder;
import cn.bingoogolapple.refreshlayout.BgarefreshLayout;
import com.google.gson.Gson;
import me.panpf.sketch.SketchImageView;
import me.panpf.sketch.sample.ResourceTable;
import me.panpf.sketch.sample.bean.UnsplashImage;
import me.panpf.sketch.sample.net.HttpCallBackListener;
import me.panpf.sketch.sample.net.NetServices;
import me.panpf.sketch.sample.slice.ImageDetailAbility;
import me.panpf.sketch.sample.slice.NetImageUtils;
import me.panpf.sketch.sample.util.DataTransferStation;
import me.panpf.sketch.sample.util.ScrollingPauseLoadManager;
import me.panpf.sketch.sample.widget.SampleImageView;
import me.panpf.sketch.shaper.CircleImageShaper;
import me.panpf.sketch.util.Log;
import me.panpf.sketch.util.SketchUtils;
import me.panpf.sketch.util.TextUtils;
import ohos.aafwk.ability.fraction.Fraction;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.agp.components.*;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;
import ohos.media.image.common.ImageInfo;
import ohos.media.image.common.Size;
import ohos.utils.IntentConstants;
import ohos.utils.net.Uri;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.logging.Logger;

/**
 * UnsplashFraction
 */
public class UnsplashFraction extends Fraction {
    private static final String REFRESH = "refresh";
    private static final String LOAD_MORE = "loadMore";
    private ListContainer listContainer;
    private MyItemProvider itemProvider;
    private int mPageIndex;
    private BgarefreshLayout mBgaRefreshLayout;
    private Component mLayoutHint;
    private Text mTextHint;
    private Button mBtnHint;

    private DataTransferStation.PageHelper dataTransferHelper;

    /**
     * isRefresh
     * @param loadType
     * @return boolean
     */
    public static boolean isRefresh(String loadType) {
        return REFRESH.equals(loadType);
    }

    /**
     * isLoadMore
     * @param loadType
     * @return boolean
     */
    public static boolean isLoadMore(String loadType) {
        return LOAD_MORE.equals(loadType);
    }

    @Override
    protected Component onComponentAttached(LayoutScatter scatter, ComponentContainer container, Intent intent) {
        Component component = scatter.parse(ResourceTable.Layout_unsplash_fraction, container, false);
        return component;
    }

    @Override
    protected void onStart(Intent intent) {
        super.onStart(intent);

        dataTransferHelper = new DataTransferStation.PageHelper(this);
        dataTransferHelper.onCreate(intent);

        Component component = getComponent();

        mLayoutHint = component.findComponentById(ResourceTable.Id_layoutHint);
        mTextHint = (Text) component.findComponentById(ResourceTable.Id_textHint);
        mBtnHint = (Button) component.findComponentById(ResourceTable.Id_btnHint);

        mBgaRefreshLayout = (BgarefreshLayout) component.findComponentById(ResourceTable.Id_bgaRefreshLayout);
        BgaStickinessRefreshViewHolder srfViewHolder = new BgaStickinessRefreshViewHolder(getComponent().getContext(), true);
        mBgaRefreshLayout.setIsShowLoadingMoreView(true);
        mBgaRefreshLayout.setPullDownRefreshEnable(true);
        mBgaRefreshLayout.setRefreshViewHolder(srfViewHolder);
        mBgaRefreshLayout.setDelegate(new BgarefreshLayout.BgarefreshLayoutDelegate() {
            @Override
            public void onBgarefreshLayoutBeginRefreshing(BgarefreshLayout bgarefreshLayout) {
                loadData(REFRESH);
            }

            @Override
            public boolean onBgarefreshLayoutBeginLoadingMore(BgarefreshLayout bgarefreshLayout) {
                loadData(LOAD_MORE);
                return false;
            }
        });

        listContainer = (ListContainer) component.findComponentById(ResourceTable.Id_listContainer);
        itemProvider = new MyItemProvider();
        listContainer.addScrolledListener(new ScrollingPauseLoadManager(getFractionAbility(), itemProvider));
        listContainer.setItemProvider(itemProvider);
        listContainer.setLongClickable(false);
        itemProvider.notifyDataChanged();

        mBgaRefreshLayout.beginRefreshing();
    }

    private void loadData(String loadType) {
        int pageIndex;
        if (LOAD_MORE.equals(loadType)) {
            pageIndex = mPageIndex + 1;
        } else if (REFRESH.equals(loadType)) {
            mPageIndex = 1;
            pageIndex = mPageIndex;
        } else {
            return;
        }

        hideHint();
        String url = null;
        try {
            url = getFractionAbility().getResourceManager().getElement(ResourceTable.String_unSplashUrl).getString();
        } catch (IOException | NotExistException | WrongTypeException e) {
            e.printStackTrace();
        }
        if (!TextUtils.isEmpty(url)) {
            getNetData(url, pageIndex, loadType);
        }
    }

    private void getNetData(String url, int pageIndex, String loadType) {
        NetServices.listUnsplashPhotos(url, pageIndex, new HttpCallBackListener() {
            @Override
            public void onResponse(String response) {
                try {
                    Gson gson = new Gson();
                    UnsplashImage[] unsplashImageArray = gson.fromJson(response, UnsplashImage[].class);
                    if ((unsplashImageArray == null) || (unsplashImageArray.length == 0)) {
                        empty();
                        return;
                    }
                    List<UnsplashImage> dataList = new ArrayList<>();
                    for (UnsplashImage unsplashImage : unsplashImageArray) {
                        dataList.add(unsplashImage);
                    }
                    {
                        if (getFractionAbility() == null) {
                            return;
                        }
                        getFractionAbility().getUITaskDispatcher().asyncDispatch(new Runnable() {
                            @Override
                            public void run() {
                                mPageIndex = pageIndex;
                                updateDataList(loadType, dataList);
                                endLoadUi(loadType);
                            }
                        });
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }

            @Override
            public void onFailure(Exception ex) {
                failed(ex, loadType);
                if (getFractionAbility() == null) {
                    return;
                }
                getFractionAbility().getUITaskDispatcher().asyncDispatch(() -> endLoadUi(loadType));
            }
        });
    }

    private void hideHint() {
        mLayoutHint.setVisibility(Component.HIDE);
    }

    private void endLoadUi(String loadType) {
        if (isLoadMore(loadType)) {
            mBgaRefreshLayout.endLoadingMore();
        } else if (isRefresh(loadType)) {
            mBgaRefreshLayout.endRefreshing();
        }
    }

    private void updateDataList(String loadType, List<UnsplashImage> dataList) {
        if (isLoadMore(loadType)) {
            itemProvider.loadMoreDataList(dataList);
        } else if (isRefresh(loadType)) {
            itemProvider.refreshDataList(dataList);
        }
    }

    private void empty() {
        if (!isEmpty()) {
            hideHint();
            return;
        }
        String text = "No photos";
        showHint(text, null);
    }

    private boolean isEmpty() {
        return (itemProvider == null) || (itemProvider.mDataList.isEmpty());
    }

    private void failed(Exception ex, String loadType) {
        if (!isEmpty()) {
            hideHint();
            return;
        }
        int errorRes = ResourceTable.Graphic_ic_error;
        String text = getCauseByException(getFractionAbility(), ex);

        showHint(text, new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                mBgaRefreshLayout.beginRefreshing();
            }
        });
    }

    private void showHint(String text, Component.ClickedListener clickedListener) {
        if (getFractionAbility() == null) {
            return;
        }
        getFractionAbility().getUITaskDispatcher().asyncDispatch(new Runnable() {
            @Override
            public void run() {
                mLayoutHint.setVisibility(Component.VISIBLE);
                if (clickedListener == null) {
                    mBtnHint.setVisibility(Component.HIDE);
                } else {
                    mBtnHint.setVisibility(Component.VISIBLE);
                }
                mTextHint.setText(text);
                mBtnHint.setClickedListener(clickedListener);
            }
        });
    }

    private String getCauseByException(Context context, Throwable exception) {
        String message;
        if (exception == null) {
            message = "网络连接异常【909】";
        } else if (exception instanceof SecurityException) {
            message = "网络连接异常【101】";
        } else if (exception instanceof UnknownHostException) {
            message = "没有网络连接";
        } else if (exception instanceof SocketTimeoutException) {
            message = "网络连接超时";
        } else if (exception instanceof FileNotFoundException) {
            message = "网络连接异常【404】";
        } else {
            message = "网络连接异常【909】";
        }
        return message;
    }

    private class MyItemProvider extends BaseItemProvider {
        private final List<UnsplashImage> mDataList = new ArrayList<>();

        void refreshDataList(List<UnsplashImage> dataList) {
            if (dataList == null) {
                return;
            }

            mDataList.clear();
            mDataList.addAll(dataList);
            notifyDataChanged();
        }

        void loadMoreDataList(List<UnsplashImage> dataList) {
            if (dataList == null) {
                return;
            }

            mDataList.addAll(dataList);

            notifyDataChanged();
        }

        @Override
        public int getCount() {
            return mDataList.size();
        }

        @Override
        public Object getItem(int i1) {
            return mDataList.get(i1);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public Component getComponent(int position, Component convertComponent, ComponentContainer componentContainer) {
            final Component cpt;
            MyViewHolder viewHolder;
            if (convertComponent == null) {
                Context context = getFractionAbility().getContext();
                cpt = LayoutScatter.getInstance(context).parse(ResourceTable.Layout_item_unsplash, componentContainer, false);
                viewHolder = new MyViewHolder();
                viewHolder.unsplashRootView = (ComponentContainer) cpt.findComponentById(ResourceTable.Id_unsplashRootView);
                viewHolder.image = (SampleImageView) cpt.findComponentById(ResourceTable.Id_image);
                viewHolder.imageHead = (SampleImageView) cpt.findComponentById(ResourceTable.Id_imageHead);
                viewHolder.textName = (Text) cpt.findComponentById(ResourceTable.Id_textName);
                viewHolder.textTime = (Text) cpt.findComponentById(ResourceTable.Id_textTime);

                viewHolder.image.setPage(SampleImageView.Page.UNSPLASH_LIST);
                viewHolder.imageHead.setPage(SampleImageView.Page.UNSPLASH_LIST);

                viewHolder.image.setClickedListener(component -> {
                });

                cpt.setTag(viewHolder);
            } else {
                cpt = convertComponent;
                viewHolder = (MyViewHolder) cpt.getTag();
            }

            setImage(position, viewHolder);

            return cpt;
        }

        private void clickUser(int position) {
            UnsplashImage unsplashImage = mDataList.get(position);
            if (unsplashImage == null) {
                return;
            }
            UnsplashImage.User user = unsplashImage.getUser();
            if (user == null) {
                return;
            }
            UnsplashImage.User.Links links = user.getLinks();
            if (links == null) {
                return;
            }
            String html = links.getHtml();
            if (TextUtils.isEmpty(html)) {
                return;
            }

            Uri uri = Uri.parse(html)
                    .makeBuilder()
                    .appendDecodedQueryParam("utm_source", "SketchSample")
                    .appendDecodedQueryParam("utm_medium", "referral")
                    .appendDecodedQueryParam("utm_campaign", "api-credit")
                    .build();
            Intent intents = new Intent();
            Operation operation = new Intent.OperationBuilder()
                    .withUri(uri)
                    .withAction(IntentConstants.ACTION_SEARCH)
                    .build();
            intents.setOperation(operation);
            getFractionAbility().startAbility(intents, 0);
        }

        private void setImage(int position, MyViewHolder viewHolder) {
            setImageParams(position, viewHolder);
            UnsplashImage unsplashImage = mDataList.get(position);
            Optional<Display> display = DisplayManager.getInstance().getDefaultDisplay(getFractionAbility());
            Point pt = new Point();
            display.get().getSize(pt);

            int width = (int) pt.getPointX();
            int imageHeight = unsplashImage.getHeight();
            int imageWidth = unsplashImage.getWidth();
            int height = (int) ((double) width * imageHeight / imageWidth * 1d);
            viewHolder.image.setWidth(width);
            viewHolder.image.setHeight(height);
            viewHolder.unsplashRootView.setHeight(height);

            // 显示图片
            PhotosFraction.displayImage(getFractionAbility(), viewHolder.image, unsplashImage.getUrls().getRegular());
            PhotosFraction.displayImage(getFractionAbility(), viewHolder.imageHead, unsplashImage.getUser().getProfileImage().getLarge());
            viewHolder.textName.setText(unsplashImage.getUser().getName());
            viewHolder.textTime.setText(unsplashImage.getFormattedUpdateDate());
        }

        private void setImageParams(int position, MyViewHolder viewHolder) {
            viewHolder.image.getOptions().setLoadingImage(ResourceTable.Media_image_loading)
                    .setErrorImage(ResourceTable.Media_image_error)
                    .setPauseDownloadImage(ResourceTable.Media_image_pause_download);
            viewHolder.imageHead.getOptions().setLoadingImage(ResourceTable.Media_image_loading)
                    .setErrorImage(ResourceTable.Media_image_error)
                    .setPauseDownloadImage(ResourceTable.Media_image_pause_download)
                    .setShaper(new CircleImageShaper().setStroke(Color.getIntColor("#FFFFFF"), 5));
            viewHolder.image.setClickedListener(component -> {
                String finalOptionsKey = ((SketchImageView) component).getOptionsKey();
                // 含有这些信息时，说明这张图片不仅仅是缩小，而是会被改变，因此不能用作loading图了
                if (finalOptionsKey.contains("Resize") || finalOptionsKey.contains("ImageProcessor") || finalOptionsKey.contains("thumbnailMode")) {
                    finalOptionsKey = null;
                }
                NetImageUtils.getInstance().datas.clear();
                List<String> images = new ArrayList<>();
                for (int i = 0; i < mDataList.size(); i++) {
                    UnsplashImage unsplashImage = mDataList.get(i);
                    if (unsplashImage == null) {
                        continue;
                    }
                    String raw = unsplashImage.getUrls().getRaw();
                    if (TextUtils.isEmpty(raw)) {
                        continue;
                    }
                    images.add(raw);
                    ImageInfo imageInfo = new ImageInfo();
                    imageInfo.size = new Size(unsplashImage.getWidth(), unsplashImage.getHeight());
                    ImageDetailAbility.MImageInfo info = new ImageDetailAbility.MImageInfo(imageInfo, SketchUtils.isGifImage(raw), raw, null);
                    NetImageUtils.getInstance().datas.put(raw, info);
                }
                String urlList = dataTransferHelper.put("urlList", images);
                ImageDetailAbility.launch(getFractionAbility(), urlList, finalOptionsKey, position);
            });
            viewHolder.imageHead.setClickRetryOnPauseDownloadEnabled(false);
            viewHolder.imageHead.setClickRetryOnDisplayErrorEnabled(false);
            Component.ClickedListener listener = new Component.ClickedListener() {
                @Override
                public void onClick(Component component) {
                    clickUser(position);
                }
            };
            viewHolder.imageHead.setClickedListener(listener);
            viewHolder.textName.setClickedListener(listener);
        }

        class MyViewHolder {
            ComponentContainer unsplashRootView;
            SampleImageView image;
            SampleImageView imageHead;
            Text textName;
            Text textTime;
        }
    }
}
